using System;
using System.Collections;
using System.Collections.Generic;
using LipingShare.LCLib.Asn1Processor;
using System.Security.Cryptography;

namespace UnityEngine.Purchasing.Security {
	internal class RSAKey {
		public RSACryptoServiceProvider rsa { get; private set; }

		public RSAKey(Asn1Node n) {
			rsa = ParseNode (n);
		}

		public RSAKey(byte[] data) {
			using (var stm = new System.IO.MemoryStream(data))
			{
				Asn1Parser parser = new Asn1Parser();
				parser.LoadData(stm);
				rsa = ParseNode(parser.RootNode);
			}
		}

		/**
		 * Public verification of a message
		 */
		public bool Verify(byte[] message, byte[] signature) {
			var sha1hash = new SHA1Managed();
			var msgHash = sha1hash.ComputeHash(message);

			// The data is already hashed so we don't need to specify a hashing algorithm.
			return rsa.VerifyHash(msgHash, null, signature);
		}

        public bool Verify256(byte[] message, byte[] signature) {
            var sha256hash = new SHA256Managed();
            var msgHash = sha256hash.ComputeHash(message);

            // The data is already hashed so we don't need to specify a hashing algorithm.
            return rsa.VerifyHash(msgHash, CryptoConfig.MapNameToOID("SHA256"), signature);
        }

		/**
		 * Parses an DER encoded RSA public key:
		 * It will only try to get the mod and the exponent
		 */
		private RSACryptoServiceProvider ParseNode(Asn1Node n) {
			if ((n.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.SEQUENCE &&
				n.ChildNodeCount == 2 &&
				(n.GetChildNode(0).Tag & Asn1Tag.TAG_MASK) == Asn1Tag.SEQUENCE &&
				(n.GetChildNode(0).GetChildNode(0).Tag & Asn1Tag.TAG_MASK) == Asn1Tag.OBJECT_IDENTIFIER &&
				n.GetChildNode(0).GetChildNode(0).GetDataStr(false) == "1.2.840.113549.1.1.1" &&
				(n.GetChildNode(1).Tag & Asn1Tag.TAG_MASK) == Asn1Tag.BIT_STRING)
			{
				var seq = n.GetChildNode(1).GetChildNode(0);
				if (seq.ChildNodeCount == 2) {
					byte[] data = seq.GetChildNode(0).Data;
					byte[] rawMod = new byte[data.Length - 1];
					System.Array.Copy(data, 1, rawMod, 0, data.Length - 1);

					var modulus = System.Convert.ToBase64String(rawMod);
					var exponent = System.Convert.ToBase64String(seq.GetChildNode(1).Data);
					var result = new RSACryptoServiceProvider ();
					result.FromXmlString(ToXML(modulus, exponent));

					return result;
				}
			}
			throw new InvalidRSAData();
		}

		private string ToXML(string modulus, string exponent) {
			return "<RSAKeyValue><Modulus>" + modulus + "</Modulus>" +
				"<Exponent>" + exponent + "</Exponent></RSAKeyValue>";
		}
	}

    /// <summary>
    /// An IAP Security exception indicating some invalid data parsing an RSA node.
    /// </summary>
	public class InvalidRSAData : IAPSecurityException {}
}
